package com.starsoft.smdc.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.starsoft.frame.core.orm.PageObject;
import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.MapUtil;
import com.starsoft.smdc.bean.SmdcDiv;
import com.starsoft.smdc.bean.SmdcDivId;
import com.starsoft.smdc.bean.SmdcEr;
import com.starsoft.smdc.bean.SmdcHandicap;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.bean.SmdcMarketDailyId;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.crawler.common.HandicapCrawlerImpl;
import com.starsoft.smdc.dao.DivDao;
import com.starsoft.smdc.dao.ErDao;
import com.starsoft.smdc.dao.MarketDailyDao;
import com.starsoft.smdc.dao.SmdcSecurityDao;
import com.starsoft.smdc.util.SecurityDataCombin;
import com.starsoft.smdc.util.SecurityDataCombin.SecurityDataCombinBean;

@Service
public class MarketDailyService {

	@Autowired
	MarketDailyDao marketDailyDao;
	
	@Autowired
	DivDao divDao;
	
	@Autowired
	ErDao erDao;
	
	@Autowired
	DivService divService;
	
	@Autowired
	HolidayService holidayService;
	
	@Autowired
	SmdcSecurityDao securityDao;
	
	@Autowired
	HandicapCrawlerImpl handicapService;

	public void saveNotNullFeild(SmdcMarketDaily market){
		SmdcMarketDaily marketDb = marketDailyDao.get(market.getId());
		if(marketDb!=null){
			MapUtil.updateBean(marketDb, MapUtil.toMap(market));
			marketDailyDao.saveOrUpdate(marketDb);
		}else {
			marketDailyDao.save(market);
		}
		updateAdjFactor(securityDao.get(market.getId().getSecId()), market.getId().getTradeDate());
	}

	public void delete(SmdcMarketDaily market) {
		marketDailyDao.delete(market);
	}
	
	public SmdcMarketDaily getMarketDaily(SmdcSecurity security, Date tradeDate){
		SmdcMarketDailyId id = new SmdcMarketDailyId(security.getSecId(), tradeDate);
		SmdcMarketDaily marketDaily = marketDailyDao.get(id);
		return marketDaily;
	}
	
	public SmdcMarketDaily getMarketDaily(String secId, Date tradeDate){
		SmdcMarketDailyId id = new SmdcMarketDailyId(secId, tradeDate);
		SmdcMarketDaily marketDaily = marketDailyDao.get(id);
		return marketDaily;
	}
	
	public SmdcMarketDaily getMarketDailyOnTime(String secId, Date tradeDate){
		SmdcMarketDaily market = this.getMarketDaily(secId, tradeDate);
		if (market==null && DateUtil.isToday(tradeDate)) {
			SmdcHandicap handicap = handicapService.getHandicap(securityDao.get(secId));
			market = caculate(handicap.toMarket());
		}
		return market;
	}

	private SmdcMarketDaily caculate(SmdcMarketDaily market) {
		String secId = market.getId().getSecId();
		Date tradeDate = market.getId().getTradeDate();
		Date lastDate = holidayService.getNextBusinessDay(tradeDate, -1);
		SmdcMarketDaily lastMarket = getMarketDaily(secId, lastDate);
		SmdcDiv todayDiv = divDao.get(new SmdcDivId(secId, tradeDate));
		
		if (lastMarket != null && lastMarket.getDivi() != null) { // 指数全收益计算
			market.setDivi(lastMarket.getDivi() * (1 + market.getPercent()));
		}
		
		if (todayDiv != null) {
			if (todayDiv.getAdjFactor() == null) {
				todayDiv.setAdjFactor(divService.caculateFactor(lastMarket.getAdjFactor(), todayDiv, market));
				todayDiv.setShareFactor(divService.caculateShareFactor(lastMarket.getShareFactor(), todayDiv));
			}
			market.setAdjFactor(todayDiv.getAdjFactor());
			market.setShareFactor(todayDiv.getShareFactor());
		} else if (lastMarket != null) {
			market.setAdjFactor(lastMarket.getAdjFactor());
			market.setShareFactor(lastMarket.getShareFactor());
		} else {
			String exceptionInfo = String.format("No market data for [%s] at [%tF]", secId, lastDate);
			throw new RuntimeException(exceptionInfo);
		}
		return market;
	}
	
	public SmdcMarketDaily getMarketDailyNotNull(String secId, Date tradeDate){
		SmdcMarketDaily marketDaily = getMarketDaily(secId, tradeDate);
		if(marketDaily == null){
			String exceptionInfo = String.format("No market data for [%s] at [%tF]", secId, tradeDate);
			throw new RuntimeException(exceptionInfo);
		}
		return marketDaily;
	}
	
	public List<SmdcMarketDaily> getDateAll(Date tradeDate){
		return marketDailyDao.getDateAll(tradeDate);
	}
	
	public List<SmdcMarketDaily> getBetweenAnd(String secId, Date beginDate, Date endDate){
		return marketDailyDao.getBetweenAnd(secId, beginDate, endDate);
	}
	
	public List<SmdcMarketDaily> getSomeBefore(String secId, int count, Date endDate){
		return marketDailyDao.getSomeBefore(secId, count, endDate);
	}
	
	public Map<String, SmdcMarketDaily> getMarketMap(List<String> secIds, Date date) {
		Map<String, SmdcMarketDaily> marketMap = new HashMap<String, SmdcMarketDaily>();
		boolean isToday = DateUtil.isToday(date);
		boolean canUseDb = isToday ? !holidayService.isBeforeClose(new Date()) : true;
		
		if (canUseDb) {
			for (String secId : secIds) {
				SmdcMarketDaily market = this.getMarketDaily(secId, date);
				if (market == null && !isToday) {
					String exceptionInfo = String.format("No market data for [%s] at [%tF]", secId, date);
					throw new RuntimeException(exceptionInfo);
				} else if (market != null) {
					marketMap.put(secId, market);
				}
			}
		}
		if (isToday) {
			List<SmdcSecurity> securityList = new ArrayList<>();
			for (String secId : secIds) {
				if (!marketMap.containsKey(secId)) {
					securityList.add(securityDao.get(secId));
				}
			}
			if(securityList.size()>0){
				List<SmdcHandicap> handicaps = handicapService.getHandicap(securityList);
				Map<String, SmdcHandicap> handicapMap = handicaps.stream().collect(Collectors.toMap(p->p.getSecId(), p->p));
				for (SmdcSecurity security : securityList) {
					String secId = security.getSecId();
					if(!handicapMap.containsKey(secId)){
						String exceptionInfo = String.format("No handicap data for [%s] at [%tF]", secId, date);
						throw new RuntimeException(exceptionInfo);
					}
					SmdcMarketDaily market = handicapMap.get(secId).toMarket();
					marketMap.put(secId, caculate(market));
				}
			}
		}
		return marketMap;
	}
	
	public void updateAdjFactor(SmdcSecurity security, Date beginDate){
		Date dateNow = new Date();
		if(beginDate.after(dateNow)){
			return;
		}
		List<SmdcMarketDaily> marketList = marketDailyDao.getAfter(security.getSecId(), beginDate);
		if (marketList == null) {
			return;
		}
		List<SmdcDiv> divList = divDao.getDivDateAfter(security, beginDate);
		List<SmdcEr> erList = erDao.getErDateAfter(security, beginDate);
		SecurityDataCombin dataCombin = new SecurityDataCombin(security);
		dataCombin.combinMarketDaily(marketList);
		dataCombin.combinDiv(divList);
		dataCombin.combinEr(erList);
		Date lastDate = DateUtil.getLast(beginDate);
		SmdcDiv baseDiv = divDao.getDivDateBeforeOne(security, lastDate);
		Double factor = 1.0;
		Double shareFactor = 1.0;
		if(baseDiv!=null){
			factor = baseDiv.getAdjFactor();
			shareFactor = baseDiv.getShareFactor();
		}
		
		for (SecurityDataCombinBean combinBean : dataCombin.getList()) {
			SmdcDiv div = combinBean.getDiv();
			SmdcMarketDaily market = combinBean.getMarketDaily();
			SmdcEr er = combinBean.getEr();
			if (div != null) {
				Date divDate = div.getId().getDivDate();
				if(divDate.after(dateNow)|| (DateUtil.isSameDay(divDate, dateNow) && holidayService.isBeforeClose(dateNow)) ){
					div.setAdjFactorBf(factor);
					div.setShareFactorBf(shareFactor);
					divDao.saveOrUpdate(div);
				} else if (market == null) {
					throw new RuntimeException("No market for " + div.getId().getSecId() + "," + div.getId().getDivDate());
				} else {
					div.setAdjFactorBf(factor);
					div.setShareFactorBf(shareFactor);
					factor = divService.caculateFactor(factor, div, market);
					shareFactor = divService.caculateShareFactor(shareFactor, div);
					div.setAdjFactor(factor);
					div.setShareFactor(shareFactor);
					divDao.saveOrUpdate(div);
				}
			}
			if(market!=null){
				market.setAdjFactor(factor);
				market.setShareFactor(shareFactor);
				marketDailyDao.saveOrUpdate(market);
			}
			if(er!=null){
				er.setAdjFactor(factor);
				er.setShareFactor(shareFactor);
				erDao.saveOrUpdate(er);
			}
		}
	}

	
	public void saveOrUpdate(SmdcMarketDaily marketDaliy) {
		marketDailyDao.saveOrUpdate(marketDaliy);
	}
	
	public Date getBeginDate(SmdcSecurity security){
		Date beginDate = marketDailyDao.getMaxDate(security);
		if(beginDate == null){
			beginDate = securityDao.getBeginDate(security);
		}else{
			beginDate = holidayService.getNextBusinessDay(beginDate, 1);
		}
		return beginDate;
	}
	
	public Date getNavBeginDate(SmdcSecurity security){
		Date beginDate = marketDailyDao.getNavMaxDate(security);
		if(beginDate == null){
			beginDate = securityDao.getBeginDate(security);
		}else{
			beginDate = holidayService.getNextBusinessDay(beginDate, 1);
		}
		return beginDate;
	}

	public Date getValuationBeginDate(SmdcSecurity security){
		Date beginDate = marketDailyDao.getValuationMaxDate(security);
		if(beginDate == null){
			beginDate = securityDao.getBeginDate(security);
		}else{
			beginDate = holidayService.getNextBusinessDay(beginDate, 1);
		}
		return beginDate;
	}
	
	public Date getHandicapBeginDate(SmdcSecurity security){
		Date beginDate = marketDailyDao.getHandicapMaxDate(security);
		if(beginDate == null){
			beginDate = securityDao.getBeginDate(security);
		}else{
			beginDate = holidayService.getNextBusinessDay(beginDate, 1);
		}
		return beginDate;
	}
	
	public Date getFullEarningBeginDate(SmdcSecurity security){
		Date beginDate = marketDailyDao.getFullEarningMaxDate(security);
		if(beginDate == null){
			beginDate = securityDao.getBeginDate(security);
		}else{
			beginDate = holidayService.getNextBusinessDay(beginDate, 1);
		}
		return beginDate;
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, Object> pageQuery(PageObject po, String secId, Date beginDate, Date endDate) {
		Map<String, Object> result = marketDailyDao.pageQuery(po, secId, beginDate, endDate);
		List<SmdcMarketDaily> rows = po.getRows();
		if(rows!=null){
			List<Object> newRows = new ArrayList<>();
			for(SmdcMarketDaily market: rows){
				newRows.add(this.convertBean(market));
			}
			result.put("rows", newRows);
		}
		return result;
	}
	
	public Map<String, Object> convertBean(SmdcMarketDaily market){
		Map<String, Object> result = MapUtil.toMap(market);
		result.remove("id");
		result.put("tradeDate", market.getId().getTradeDate());
		result.put("secId", market.getId().getSecId());
		result.put("secName", securityDao.get(market.getId().getSecId()).getSecName());
		return result;
	}

}
